package com.zoe.onelink.core.web.jackson.serializer;

import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.BeanProperty;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.ser.ContextualSerializer;
import com.fasterxml.jackson.databind.ser.std.StringSerializer;
import com.zoe.onelink.annotation.web.jackson.encryptor.EncryptParam;
import com.zoe.onelink.annotation.data.encryptor.IEncryptor;
import com.zoe.onelink.common.enumeration.ExceptionCodeEnum;
import com.zoe.onelink.common.exception.ApiException;
import com.zoe.onelink.core.property.OnelinkProperties;
import com.zoe.onelink.core.encryptor.EncryptorFactory;
import com.zoe.onelink.core.encryptor.EncryptorManager;
import com.zoe.onelink.util.StrUtil;
import lombok.Setter;

import java.io.IOException;
import java.util.Optional;

/**
 * <p>标题: 加密序列化器</p>
 * <p>描述: </p>
 * <p>版权: Copyright (c) 2021</p>
 * <p>公司: 智业软件股份有限公司</p>
 *
 * @version: 1.0
 * @author: ruansheng
 * @date: 2021-11-12
 */
@Setter
public class JacksonEncryptSerializer extends JsonSerializer<String> implements ContextualSerializer {

    private final OnelinkProperties.Web.Encrypt encryptProp;
    private IEncryptor encryptor;
    private String algorithmName;

    public JacksonEncryptSerializer(OnelinkProperties.Web.Encrypt encryptProp) {
        this.encryptProp = encryptProp;
    }

    @Override
    public void serialize(String value, JsonGenerator gen, SerializerProvider serializers) throws IOException {

        // 如果字段加密未启用,直接写入原参数
        if (!this.encryptProp.isEnabled()) {
            gen.writeString(value);
            return;
        }

        // 加密算法实例不存在
        if (this.encryptor == null) {
            throw ApiException.createEx(ExceptionCodeEnum.SERIALIZATION_ENCRYPT_NO_SUCH_ALGORITHM, this.algorithmName);
        }

        try {
            String encrypt = this.encryptor.encrypt(value);
            gen.writeString(encrypt);
        } catch (ApiException e) {
            throw e;
        } catch (Exception e) {
            throw ApiException.createEx(ExceptionCodeEnum.SERIALIZATION_ENCRYPT_VALUE_FAILURE, gen.getOutputContext().getCurrentName());
        }
    }

    @Override
    public JsonSerializer<?> createContextual(SerializerProvider prov, BeanProperty property) throws JsonMappingException {
        // 获取字段注解
        EncryptParam encryptParam = Optional.ofNullable(property.getAnnotation(EncryptParam.class))
                .orElse(property.getContextAnnotation(EncryptParam.class));
        if (encryptParam == null) {
            return new StringSerializer();
        }
        // 算法名称
        String algorithmName = encryptParam.algorithm().name().toUpperCase();
        if (StrUtil.isBlank(algorithmName)) {
            throw ApiException.createEx(ExceptionCodeEnum.SERIALIZATION_ENCRYPT_ALGORITHM_NOT_NULL, property.getName());
        }
        // 根据算法名称创建对应的算法
        IEncryptor encryptor = EncryptorFactory.createEncryptor(encryptParam.algorithm());
        if (encryptor == null) {
            throw ApiException.createEx(ExceptionCodeEnum.SERIALIZATION_ENCRYPT_NO_SUCH_ALGORITHM, property.getName(), algorithmName);
        }
        // 向加密实例管理器注册对应的实例
        EncryptorManager.register(algorithmName, encryptor);
        // 创建序加密序列化实例
        JacksonEncryptSerializer jacksonEncryptSerializer = new JacksonEncryptSerializer(this.encryptProp);
        jacksonEncryptSerializer.setAlgorithmName(algorithmName);
        jacksonEncryptSerializer.setEncryptor(encryptor);
        return jacksonEncryptSerializer;
    }
}
